package com.xiaouyudeguang.common.service.impl;

import cn.easyes.common.params.SFunction;
import cn.easyes.core.biz.EsPageInfo;
import cn.easyes.core.conditions.LambdaEsQueryWrapper;
import cn.easyes.core.conditions.LambdaEsUpdateWrapper;
import com.xiaouyudeguang.common.annotation.DataScope;
import com.xiaouyudeguang.common.dao.AbstractDao;
import com.xiaouyudeguang.common.event.LogEvent;
import com.xiaouyudeguang.common.exception.BusinessException;
import com.xiaouyudeguang.common.request.RequestDto;
import com.xiaouyudeguang.common.service.AbstractService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.domain.PageImpl;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

@Transactional(rollbackFor = Exception.class)
public abstract class AbstractServiceImpl<Dao extends AbstractDao<T>, T> implements AbstractService<Dao, T> {

    protected Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    protected Dao baseDao;

    @Autowired
    protected ApplicationContext context;

    @Autowired
    protected HttpServletRequest request;

    protected final LambdaEsUpdateWrapper<T> updateWrapper() {
        return new LambdaEsUpdateWrapper<>();
    }

    protected final LambdaEsQueryWrapper<T> lambdaWrapper() {
        return new LambdaEsQueryWrapper<>();
    }

    public abstract LambdaEsQueryWrapper<T> lambdaWrapper(String tenantId, String appId, RequestDto<T> requestDto) throws Exception;

    @Override
    public T getById(String tenantId, String appId, String id) throws Exception {
        return baseDao.selectById(id);
    }

    @Override
    public List<T> getByIds(String tenantId, String appId, Set<String> ids) throws Exception {
        return baseDao.selectBatchIds(ids.stream().map(Long::valueOf).collect(Collectors.toSet()));
    }

    @Override
    public Boolean deleteById(String tenantId, String appId, String id) throws Exception {
        return baseDao.deleteById(id) > 0;
    }

    @Override
    public Boolean deleteByIds(String tenantId, String appId, Set<String> ids) throws Exception {
        return baseDao.deleteBatchIds(ids) > 0;
    }

    @Override
    public Boolean delete(String tenantId, String appId, SFunction<T, ?> function, Object value) throws Exception {
        return baseDao.delete(lambdaWrapper().eq(function, value)) > 0;
    }

    @Override
    public Boolean delete(String tenantId, String appId, SFunction<T, ?> function, Set<?> values) throws Exception {
        return baseDao.delete(lambdaWrapper().in(function, values)) > 0;
    }

    @Override
    @DataScope(DataScope.Scope.UPDATE)
    public T updateById(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        T data = requestDto.getData();
        baseDao.updateById(data);
        return data;
    }

    @Override
    @DataScope(DataScope.Scope.UPDATE)
    public List<T> updateByIds(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        T data = requestDto.getData();
        baseDao.updateById(data);
        return Collections.singletonList(data);
    }

    @Override
    @DataScope(DataScope.Scope.UPDATE)
    public List<T> updateBatch(String tenantId, String appId, List<T> dataList) throws Exception {
        for (T data : dataList) {
            updateById(tenantId, appId, RequestDto.getRequest(data));
        }
        return dataList;
    }

    @Override
    @DataScope(DataScope.Scope.MODIFY)
    public T modify(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        T data = requestDto.getData();
        baseDao.updateById(data);
        return data;
    }

    @Override
    @DataScope(DataScope.Scope.SAVE)
    public T save(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        T data = requestDto.getData();
        baseDao.insert(data);
        return data;
    }

    @Override
    @DataScope(DataScope.Scope.SAVE)
    public List<T> saveBatch(String tenantId, String appId, List<T> dataList) throws Exception {
        for (T data : dataList) {
            save(tenantId, appId, RequestDto.getRequest(data));
        }
        return dataList;
    }

    @Override
    public List<T> upload(String tenantId, String appId, List<T> dataList) throws Exception {
        if (dataList.size() > 100) {
            throw new BusinessException("单次导入不能超过100条");
        }
        for (T data : dataList) {
            save(tenantId, appId, RequestDto.getRequest(data));
        }
        return dataList;
    }

    @Override
    public List<T> export(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return isNotEmpty(requestDto.getIds()) ? getByIds(tenantId, appId, requestDto.getIds()) : page(tenantId, appId, requestDto).getContent();
    }

    @Override
    public T getOne(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return baseDao.selectOne(lambdaWrapper(tenantId, appId, requestDto));
    }

    @Override
    public T getOne(String tenantId, String appId, SFunction<T, ?> function, Object value) throws Exception {
        return value == null ? null : baseDao.selectOne(lambdaWrapper().eq(function, value));
    }

    @Override
    public List<T> list(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        return baseDao.selectList(lambdaWrapper(tenantId, appId, requestDto));
    }

    @Override
    public List<T> list(String tenantId, String appId, SFunction<T, ?> function, Object value) throws Exception {
        return value == null ? emptyList() : baseDao.selectList(lambdaWrapper().eq(function, value));
    }

    @Override
    public List<T> like(String tenantId, String appId, SFunction<T, ?> function, Object value) throws Exception {
        return value == null ? emptyList() : baseDao.selectList(lambdaWrapper().like(function, value));
    }

    @Override
    public List<T> list(String tenantId, String appId, SFunction<T, ?> function, Set<?> values) throws Exception {
        return isEmpty(values) ? emptyList() : baseDao.selectList(lambdaWrapper().in(function, values));
    }

    @Override
    public List<T> listAll(String tenantId, String appId) throws Exception {
        return baseDao.selectList(new LambdaEsQueryWrapper<>());
    }

    @Override
    public PageImpl<T> page(String tenantId, String appId, RequestDto<T> requestDto) throws Exception {
        EsPageInfo<T> page = baseDao.pageQuery(lambdaWrapper(tenantId, appId, requestDto), requestDto.getCurrent(), requestDto.getSize());
        List<T> recordList = page.getList();
        if (isEmpty(recordList)) {
            return emptyPage();
        }
        fillData(tenantId, appId, recordList);
        return page(recordList, page.getTotal());
    }

    @Override
    public PageImpl<T> emptyPage() {
        return AbstractService.super.emptyPage();
    }

    @Override
    public PageImpl<T> page(List<T> records, long total) {
        return AbstractService.super.page(records, total);
    }

    public void publish(String tenantId, String appId, String id, T data) {
        context.publishEvent(new LogEvent(tenantId, appId, id, data));
    }

    /**
     * 批量查询
     *
     * @param lambdaEsQueryWrapper
     * @param function
     * @param value
     */
    public void fillBatchQuery(LambdaEsQueryWrapper<T> lambdaEsQueryWrapper, SFunction<T, ?> function, String value) {
        if (isNotBlank(value)) {
            if (value.contains(",")) {
                lambdaEsQueryWrapper.in(function, Arrays.asList(value.split(",")));
            } else if (value.contains("，")) {
                lambdaEsQueryWrapper.in(function, Arrays.asList(value.split("，")));
            } else if (value.contains(";")) {
                lambdaEsQueryWrapper.in(function, Arrays.asList(value.split(";")));
            } else if (value.contains("；")) {
                lambdaEsQueryWrapper.in(function, Arrays.asList(value.split("；")));
            } else if (value.contains("&")) {
                lambdaEsQueryWrapper.in(function, Arrays.asList(value.split("&")));
            } else if (value.contains(" ")) {
                lambdaEsQueryWrapper.in(function, Arrays.asList(value.split(" ")));
            } else {
                lambdaEsQueryWrapper.like(function, value);
            }
        }
    }

    /**
     * 范围查询
     *
     * @param lambdaEsQueryWrapper
     * @param function
     * @param minValue
     * @param maxValue
     */
    protected void fillLimitQuery(LambdaEsQueryWrapper<T> lambdaEsQueryWrapper, SFunction<T, ?> function, Object minValue, Object maxValue) {
        //下单时间
        lambdaEsQueryWrapper.gt(minValue != null, function, minValue);
        lambdaEsQueryWrapper.lt(maxValue != null, function, maxValue);
    }

    protected void fillData(String tenantId, String appId, T data) {
    }

    protected void fillData(String tenantId, String appId, List<T> dataList) {
    }
}
